package com.com.gmail.at.vssabinin.client;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

public class Protocol {
	
	public class Message {
		private String type;
		private Object object;
		public Message(String t, Object obj) {
			this.setType(t);
			this.setObject(obj);
		}
		public Object getObject() {
			return object;
		}
		public void setObject(Object object) {
			this.object = object;
		}
		public String getType() {
			return type;
		}
		public void setType(String type) {
			this.type = type;
		}
		
	}
	
	private byte[] integerToByteArray(int integer) {
		return ByteBuffer.allocate(4).putInt(integer).array();
	}
	
	public <T extends Serializable> byte[] createMessage(Message mes) {
		List<Byte> message = new ArrayList<Byte>();
		byte[] answer = null;
		byte[] stringLength = this.integerToByteArray(mes.type.getBytes().length);
		ByteArrayOutputStream os = new ByteArrayOutputStream();
		ObjectOutputStream out = null;
		try {
			out = new ObjectOutputStream(os);
			out.writeObject(mes.object);
			byte[] byteObj = os.toByteArray();
			byte[] totalLength = this.integerToByteArray(byteObj.length + mes.type.getBytes().length + 4 + 4);
			for (int i = 0; i < 4; i++) {
				message.add(totalLength[i]);
			}
			for (int i = 0; i < 4; i++) {
				message.add(stringLength[i]);
			}
			for (Byte b : mes.type.getBytes()) {
				message.add(b);
			}
			for (Byte b : byteObj) {
				message.add(b);
			}
			answer = new byte[message.size()];
			for(int i = 0; i < message.size(); i++) {
				answer[i] = message.get(i);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				out.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			try {
				os.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return answer;
	}
	
	public Integer decodeMessageLength(ByteBuffer buffer) {
		if (buffer.remaining() > 4) {
			byte[] totalLength = new byte[4];
			for(int i = 0; i < 4; i++) {
				totalLength[i] = buffer.array()[i];
			}
			return ByteBuffer.wrap(totalLength).getInt();
		}
		return null;
	}
	
	public boolean isMessageComplete(ByteBuffer buffer) {
		if(buffer.limit() == this.decodeMessageLength(buffer)) {
			return true;
		}
		return false;
	}
	
	public Message decodeMessage(ByteBuffer buffer) {
		Message message = null;
		byte[] stringLengthByte = new byte[4];
		for(int i = 0; i < 4; i++) {
			stringLengthByte[i] = buffer.array()[i + 4];
		}
		ByteBuffer b = ByteBuffer.wrap(stringLengthByte);
		Integer stringLengthInteger = b.getInt(); 
		byte[] byteString = new byte[stringLengthInteger];
		for (int i = 0; i < stringLengthInteger; i++) {
			byteString[i] = buffer.array()[i + 8];
		}
		String type =  new String(byteString, Charset.defaultCharset());
		byte[] byteObject = new byte[this.decodeMessageLength(buffer) - 8 - stringLengthInteger];
		for (int i = 0; i < byteObject.length; i++) {
			byteObject[i] = buffer.array()[i + 8 + stringLengthInteger];
		}
		ByteArrayInputStream is = new ByteArrayInputStream(byteObject);
		ObjectInputStream input = null;
		try {
			input = new ObjectInputStream(is);
			message = new Message(type, input.readObject());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				input.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			try {
				is.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return message; 	
	}
}
